/*I2S routines*/

/*Usart header file*/
#include "i2s.h"

/*Global Variable definition*/
tI2sData xI2sData;

/*Constant definition*/

eI2sInitError eI2sInit( pI2sInitData data )
{
    AT91PS_SSC pSSC = AT91C_BASE_SSC;
    
    /*Disable interrupts*/
    AT91F_SSC_DisableIt(pSSC, AT91C_SSC_ENDTX | AT91C_SSC_ENDRX);
    
    /*Enable I2S PIO (SI, SO, SCK)*/
    AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, (unsigned portLONG) AT91C_PA15_TF | (unsigned portLONG) AT91C_PA16_TK | (unsigned portLONG) AT91C_PA17_TD, 0);

    /*Enable I2S Clocks*/
    AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SSC);

    /*Reset SSC and disable TX and RX*/
    pSSC->SSC_CR = AT91C_SSC_SWRST | AT91C_SSC_RXDIS | AT91C_SSC_TXDIS ;

    /*Disable PDC for I2S*/
    AT91F_PDC_DisableRx(AT91C_BASE_PDC_SSC);
    AT91F_PDC_DisableTx(AT91C_BASE_PDC_SSC);

    /*Initialize PDC TX and RX*/
    AT91F_PDC_SetTx(AT91C_BASE_PDC_SSC, 0, 0);
    AT91F_PDC_SetNextTx(AT91C_BASE_PDC_SSC, 0, 0);
    AT91F_PDC_SetRx(AT91C_BASE_PDC_SSC, 0, 0);
	 AT91F_PDC_SetNextRx(AT91C_BASE_PDC_SSC, 0, 0);

    /*Define the Clock Mode Register*/
    AT91F_SSC_SetBaudrate(pSSC, (unsigned portLONG) configCPU_CLOCK_HZ, (unsigned portLONG) data->config.samplingFreq * ((unsigned portLONG) data->config.bitsBySLot * (unsigned portLONG) data->config.slotByFrame));

    /*If enabled, configure the Transmit Frame Mode Register*/
	if (data->config.txEnable)
    {
        pSSC->SSC_TFMR = I2S_ASY_TX_FRAME_SETTING(data->config.bitsBySLot, data->config.slotByFrame);
        pSSC->SSC_TCMR = I2S_ASY_MASTER_TX_SETTING(data->config.bitsBySLot, data->config.slotByFrame);

        AT91F_SSC_EnableTx(pSSC);
        AT91F_PDC_EnableTx(AT91C_BASE_PDC_SSC);

        /*Create and initialize TX semaphores*/
        if (!xI2sData.config.txEnabled)
        {
            vSemaphoreCreateBinary(xI2sData.send.mutex.xSemaphore);
            vSemaphoreCreateBinary(xI2sData.send.event.xSemaphore);
            xI2sData.send.mutex.xBlockTime = INITIAL_OS_BLOCK_TIME_MAX;
        }

        xI2sData.config.txEnabled = 1;
    }
    
    /*If enabled, configure the Receive Frame Mode Register*/
	if (data->config.rxEnable)
    {
        pSSC->SSC_RFMR = I2S_ASY_TX_FRAME_SETTING(data->config.bitsBySLot, data->config.slotByFrame);
        pSSC->SSC_RCMR = I2S_ASY_MASTER_TX_SETTING(data->config.bitsBySLot, data->config.slotByFrame);

        AT91F_SSC_EnableRx(pSSC);
        AT91F_PDC_EnableRx(AT91C_BASE_PDC_SSC);

        /*Create and initialize RX semaphores*/
        if (!xI2sData.config.rxEnabled)
        {
            vSemaphoreCreateBinary(xI2sData.receive.mutex.xSemaphore);
            vSemaphoreCreateBinary(xI2sData.receive.event.xSemaphore);
            xI2sData.receive.mutex.xBlockTime = INITIAL_OS_BLOCK_TIME_MAX;
        }

        xI2sData.config.rxEnabled = 1;
    }

    /*Configure and enable interrupts*/
    AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SSC, AT91C_AIC_PRIOR_LOWEST + 2, AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE, ( void (*)(void) ) vI2sISREntry);
    AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SSC); 
         
    return i2sInitSuccess;
}

void vI2sClose(void) //TODO el close cerrando lo q abri
{
AT91PS_SSC pSSC = AT91C_BASE_SSC;

    /*Disable I2S PIO (SI, SO, SCK)*/
    AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, (unsigned portLONG) AT91C_PA15_TF | (unsigned portLONG) AT91C_PA16_TK | (unsigned portLONG) AT91C_PA17_TD);

    /*Disable PDC for I2S*/
    AT91F_PDC_DisableRx(AT91C_BASE_PDC_SSC);
    AT91F_PDC_DisableTx(AT91C_BASE_PDC_SSC);

    /*Disable I2S Clocks*/
    AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SSC);

    /*Disable TX and RX*/
    pSSC->SSC_CR = AT91C_SSC_RXDIS | AT91C_SSC_TXDIS ;
    AT91F_SSC_DisableTx(pSSC);
    AT91F_SSC_DisableRx(pSSC);

    /*Disable interrupts*/
    AT91F_SSC_DisableIt(AT91C_BASE_SSC, AT91C_SSC_ENDTX | AT91C_SSC_ENDRX);
    AT91F_AIC_DisableIt(AT91C_BASE_AIC, AT91C_ID_SSC);

    /*Release Semaphores*/
    vQueueDelete(xI2sData.send.mutex.xSemaphore);
    vQueueDelete(xI2sData.send.event.xSemaphore);
    vQueueDelete(xI2sData.receive.mutex.xSemaphore);
    vQueueDelete(xI2sData.receive.event.xSemaphore);

    /*Set Module as uninit*/
    xI2sData.config.txEnabled = 0;
    xI2sData.config.rxEnabled = 0;
}

unsigned portCHAR cSendFrame(pI2sSendData pData)
{
unsigned portCHAR errCode;
int size, nsize;  

    if (!xI2sData.config.txEnabled) {return ERR_DEV_UNINIT;}

    if (xSemaphoreTake(xI2sData.send.mutex.xSemaphore, (portTickType) xI2sData.send.mutex.xBlockTime ) != pdTRUE)
    {
        return ERR_SEM_TIMEOUT;
    }

    /*Disable PDC while configuring or on the fly*/
    #if I2S_SENDRCV_DIS_W_SETTING == 1
    AT91F_PDC_DisableTx(AT91C_BASE_PDC_SSC);
    #endif

    /*Take blocking semaphore*/
    xSemaphoreTake(xI2sData.send.event.xSemaphore, 0);

    /*Load wait block time*/
    xI2sData.send.event.xBlockTime = pData->xBlockTime;

    /*Send the current and the next frame*/
    if ((AT91C_BASE_SSC->SSC_TNCR == 0) && (AT91C_BASE_SSC->SSC_TCR == 0))
    {
      nsize = pData->size >> 1;
		size = pData->size - nsize;
		AT91F_PDC_SetTx(AT91C_BASE_PDC_SSC, (char *) pData->data, size);
		if (nsize > 0)
		{
        	AT91F_PDC_SetNextTx(AT91C_BASE_PDC_SSC, (char *) &(pData->data[size]), nsize);
		}
    }
    else if (AT91C_BASE_SSC->SSC_TNCR == 0)
    {
        AT91F_PDC_SetNextTx(AT91C_BASE_PDC_SSC, (char *) pData->data, pData->size);
    }
        
    /*Enable Transmission*/
    #if I2S_SENDRCV_DIS_W_SETTING == 1
    AT91F_PDC_EnableTx(AT91C_BASE_PDC_SSC);
    #endif

    /*Enable Interrupt Mask*/
    AT91F_SSC_EnableIt(AT91C_BASE_SSC, AT91C_SSC_ENDTX);

    /*Block to wait results*/
    if (xSemaphoreTake(xI2sData.send.event.xSemaphore, (portTickType) xI2sData.send.event.xBlockTime ) == pdTRUE)
    {
        errCode = SUCCESS;
    }
    else { errCode = ERR_TASK_TIMEOUT; }

    /*Release mutex*/
    xSemaphoreGive(xI2sData.send.mutex.xSemaphore);
   
    return errCode;
}

unsigned portCHAR cReceiveFrame(pI2sReceiveData pData)
{
unsigned portCHAR errCode; 
int size, nsize; 

    if (!xI2sData.config.rxEnabled) {return ERR_DEV_UNINIT;}

    if (xSemaphoreTake(xI2sData.receive.mutex.xSemaphore, (portTickType) xI2sData.receive.mutex.xBlockTime ) != pdTRUE)
   {
        return ERR_SEM_TIMEOUT;
   }

   /*Disable PDC while configuring or on the fly*/
   #if I2S_SENDRCV_DIS_W_SETTING == 1
   AT91F_PDC_DisableRx(AT91C_BASE_PDC_I2S);
   #endif

   /*Take blocking semaphore*/
   xSemaphoreTake(xI2sData.receive.event.xSemaphore, 0);

   /*Load wait block time*/
   xI2sData.send.event.xBlockTime = pData->xBlockTime;
    
   /*Receive the current and the next frame*/
   if (AT91C_BASE_SSC->SSC_RNCR == 0) 
   {
      nsize = pData->size >> 1; 
		size = pData->size - nsize;
        AT91F_PDC_SetRx(AT91C_BASE_PDC_SSC, (char *) pData->data, size);
		if (nsize > 0)
		{
        	AT91F_PDC_SetNextRx(AT91C_BASE_PDC_SSC, (char *) pData->data, nsize);
		}
    }
    else 
    {
        AT91F_PDC_SetNextRx(AT91C_BASE_PDC_SSC, (char *) pData->data, pData->size);
    }

    /*Enable Reception*/										  
    #if I2S_SENDRCV_DIS_W_SETTING == 1
    AT91F_PDC_EnableRx(AT91C_BASE_PDC_I2S);
    #endif

    /*Enable Interrupt Mask*/
    AT91F_SSC_EnableIt(AT91C_BASE_SSC, AT91C_SSC_ENDRX);

    /*Block to wait results*/
    if (xSemaphoreTake(xI2sData.receive.event.xSemaphore, (portTickType) xI2sData.receive.event.xBlockTime ) == pdTRUE)
    {
        errCode = SUCCESS;
    }
    else { errCode = ERR_TASK_TIMEOUT; }

    /*Release mutex*/
    xSemaphoreGive(xI2sData.receive.mutex.xSemaphore);

    return errCode;
}

void vI2sISR(void)
{
    unsigned portLONG ulStatus;
    portBASE_TYPE xTaskWokenByI2sSend = pdFALSE;
    portBASE_TYPE xTaskWokenByI2sReceive = pdFALSE;
    AT91PS_SSC pSSC = AT91C_BASE_SSC;
    
    /* Original cause of the interruption */
    ulStatus = pSSC->SSC_SR &= pSSC->SSC_IMR;

    /*Is a Transmit interrupt*/
    if (ulStatus & AT91C_SSC_ENDTX)
    {
        /*Nothing more to transmit*/
        if (pSSC->SSC_TCR == 0) {AT91F_SSC_DisableIt(pSSC, AT91C_SSC_ENDTX);}
        
        /*Signal semaphore*/
        xSemaphoreGiveFromISR(xI2sData.send.event.xSemaphore, &xTaskWokenByI2sSend);
    }

    /*Is a Receive interrupt*/
    if (ulStatus & AT91C_SSC_ENDRX)
    {
        /*Nothing more to receive*/
        if (pSSC->SSC_RCR == 0) {AT91F_SSC_DisableIt(pSSC, AT91C_SSC_ENDRX);}
        
        /*Signal semaphore*/
        xSemaphoreGiveFromISR(xI2sData.receive.event.xSemaphore, &xTaskWokenByI2sReceive);
    }
    
    /* End the interrupt in the AIC. */
    AT91C_BASE_AIC->AIC_EOICR = 0;
    
    /* If a task was woken by either a character being received or a character 
    being transmitted then we may need to switch to another task. */
    portEND_SWITCHING_ISR(xTaskWokenByI2sSend || xTaskWokenByI2sReceive);
}
